package com.tools.common.container;

import com.tools.common.container.r.OnlyReadHashMap;
import com.tools.common.container.r.OnlyReadLinkedMap;
import com.tools.common.container.r.OnlyReadTreeMap;
import com.tools.common.exception.InvalidOperationException;
import com.tools.common.object.Note;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 自定义 Map 键值对集合容器的
 * 抽象类实现，对 OwnPairs 接口
 * 的方法做了默认实现，且兼容和扩展了
 * 原生 Map 集合的 API，使该自定义
 * 的 Map 容器被继承的子类的代码结构
 * 更加干净整洁
 * */
@Note("自定义 Map 键值对集合容器的抽象类实现，对 OwnPairs 接口" +
        "的方法做了默认实现，且兼容和扩展了原生 Map 集合的 API，" +
        "使该自定义的 Map 容器被继承的子类的代码结构更加干净整洁")
public abstract class AbsOwnPair<K, V> implements OwnPairs<K, V> {

    @Note("数据存放的容器实体，使用 Map 集合做包装")
    private Map<K, V> body;

    @Note("针对 body 属性，使用它的哪个实现类实现它，默认是 HashMap 集合")
    private MapEnum implEnum = MapEnum.HASH;

    @Note("如果 body 不为空，是否需要对数据内容做排序，这里用于保存排序的比较器")
    private Comparator<? super K> comparator;

    /* *******************************************************************************************
     *
     *          构造器
     *
     * *******************************************************************************************
     * */

    public AbsOwnPair(boolean initBody) {
        if(initBody) {
            this.body = this.implEnum.newMap();
        }
    }


    public AbsOwnPair(boolean initBody, int initSize) {
        if(initBody){
            this.body = this.implEnum.newMap(initSize);
        }
    }


    public AbsOwnPair(boolean initBody, Map<? extends K, ? extends V> map) {
        if(initBody) {
            this.body = this.implEnum.newMap(map);
        }
    }


    public AbsOwnPair(boolean initBody, OwnPairs<? extends K, ? extends V> pair) {
        if(initBody) {
            this.body = this.implEnum.newMap(pair);
        }
    }

    public AbsOwnPair(boolean initBody, MapEnum implEnum) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        if(initBody) {
            this.body = this.implEnum.newMap();
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, int initSize) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        if(initBody) {
            this.body = this.implEnum.newMap(initSize);
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, int initSize, Comparator<? super K> c) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.comparator = c;
        if(initBody) {
            this.body = this.implEnum.newMap(initSize, c);
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, Comparator<? super K> c) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.comparator = c;
        if(initBody) {
            this.body = this.implEnum.newMap(c);
        }
    }

    public AbsOwnPair(boolean initBody, MapEnum implEnum, Map<? extends K, ? extends V> map) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        if(initBody) {
            this.body = this.implEnum.newMap(map);
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, Map<? extends K, ? extends V> map, Comparator<? super K> c) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.comparator = c;
        if(initBody) {
            this.body = this.implEnum.newMap(map, c);
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, OwnPairs<? extends K, ? extends V> pair) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        if(initBody) {
            this.body = this.implEnum.newMap(pair);
        }
    }


    public AbsOwnPair(boolean initBody, MapEnum implEnum, OwnPairs<? extends K, ? extends V> pair, Comparator<? super K> c) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.comparator = c;
        if(initBody) {
            this.body = this.implEnum.newMap(pair, c);
        }
    }



    /* *******************************************************************************************
     *
     *          链式增删方法
     *
     * *******************************************************************************************
     * */

    @Override
    public AbsOwnPair<K, V> put(K key, V value) {
        this.body.put(key, value);
        return this;
    }

    @Override
    public AbsOwnPair<K, V> putIfAbsent(K sourceKey, V backupValue) {
        this.body.putIfAbsent(sourceKey, backupValue);
        return this;
    }

    @Override
    public AbsOwnPair<K, V> putAll(Map<? extends K, ? extends V> map) {
        if(map == null || map.isEmpty()) return this;
        this.body.putAll(map);
        return this;
    }

    @Override
    public AbsOwnPair<K, V> putAll(OwnPairs<? extends K, ? extends V> pair) {
        if(pair == null || pair.isEmpty()) return this;
        for (Map.Entry<? extends K, ? extends V> e : pair.entrySet()) {
            this.body.put(e.getKey(), e.getValue());
        }
        return this;
    }

    @Override
    public AbsOwnPair<K, V> remove(Object key) {
        this.body.remove(key);
        return this;
    }

    @Override
    public AbsOwnPair<K, V> removeAll(Object[] keys) {
        if(keys == null || keys.length == 0) return this;
        for (Object key : keys) {
            this.body.remove(key);
        }
        return this;
    }

    @Override
    public OwnPairs<K, V> removeAll(Collection<?> keys) {
        if(keys == null || keys.isEmpty()) return this;
        for (Object key : keys) {
            this.body.remove(key);
        }
        return this;
    }

    @Override
    public OwnPairs<K, V> removeAll(OwnRows<?> keys) {
        if(keys == null || keys.isEmpty()) return this;
        for (Object key : keys) {
            this.body.remove(key);
        }
        return this;
    }

    @Override
    public AbsOwnPair<K, V> removeAll(Iterable<?> keys) {
        if(keys == null) return this;
        for (Object key : keys) {
            this.body.remove(key);
        }
        return this;
    }

    @Override
    public AbsOwnPair<K, V> remove(K key, V value) {
        this.body.remove(key, value);
        return this;
    }

    @Override
    public AbsOwnPair<K, V> removeAll(Map<? extends K, ? extends V> map) {
        if(map == null || map.isEmpty()) return this;
        for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
            this.body.remove(e.getKey(), e.getValue());
        }
        return this;
    }

    @Override
    public AbsOwnPair<K, V> removeAll(OwnPairs<? extends K, ? extends V> pair) {
        if(pair != null && !pair.isEmpty())  {
            for (Map.Entry<? extends K, ? extends V> e : pair.entrySet()) {
                this.body.remove(e.getKey(), e.getValue());
            }
        }
        return this;
    }

    public AbsOwnPair<K,V> replace(K key, V newValue) {
        this.body.replace(key, newValue);
        return this;
    }


    public AbsOwnPair<K,V> replace(K key, V oldValue, V newValue) {
        this.body.replace(key, oldValue, newValue);
        return this;
    }

    /* *******************************************************************************************
     *
     *          接口方法
     *
     * *******************************************************************************************
     * */

    @Override
    public V get(Object key) {
        if(body == null || body.isEmpty()) return null;
        return this.body.get(key);
    }

    @Override
    public V getOrDefault(Object key, V defaultValue) {
        if(body == null || body.isEmpty()) return null;
        return body.getOrDefault(key, defaultValue);
    }

    @Override
    public boolean containsKey(K key) {
        if(body == null || body.isEmpty()) return false;
        return this.body.containsKey(key);
    }

    @Override
    public boolean containsAllKey(K[] keys) {
        if(keys == null || keys.length == 0) return false;
        if(body == null || body.isEmpty()) return false;
        for(K key : keys) {
            if(!this.body.containsKey(key)) return false;
        }
        return true;
    }

    @Override
    public boolean containsAllKey(Collection<? extends K> keys) {
        if(keys == null || keys.isEmpty()) return false;
        if(body == null || body.isEmpty()) return false;
        for (K key : keys) if(!this.body.containsKey(key)) return false;
        return true;
    }

    @Override
    public boolean containsAllKey(OwnRows<? extends K> keys) {
        if(keys == null || keys.isEmpty()) return false;
        if(body != null && !body.isEmpty()) {
            for (K key : keys) if(!this.body.containsKey(key)) return false;
            return true;
        }
        return false;
    }

    @Override
    public boolean containsAllKey(Iterable<? extends K> keys) {
        if(keys == null) return false;
        Iterator<? extends K> iterator = keys.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(!body.containsKey(iterator.next())) return false;
        }
        return true;
    }

    @Override
    public boolean containsAnyKey(K[] keys) {
        if(body == null || body.isEmpty()) return false;
        for(K key : keys) {
            if(this.body.containsKey(key)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyKey(Collection<? extends K> keys) {
        if(keys == null || keys.isEmpty()) return false;
        if(body == null || body.isEmpty()) return false;
        for (K key : keys) if(this.body.containsKey(key)) return true;
        return false;
    }

    @Override
    public boolean containsAnyKey(OwnRows<? extends K> keys) {
        if(keys == null || keys.isEmpty()) return false;
        if(body != null && !body.isEmpty()) {
            for (K key : keys) if(this.body.containsKey(key)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyKey(Iterable<? extends K> keys) {
        if(keys == null) return false;
        Iterator<? extends K> iterator = keys.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(body.containsKey(iterator.next())) return true;
        }
        return false;
    }

    @Override
    public boolean containsValue(V value) {
        if(body == null || body.isEmpty()) return false;
        return this.body.containsValue(value);
    }

    @Override
    public boolean containsAllValue(V[] values) {
        if(body == null || body.isEmpty()) return false;
        for(V value : values) {
            if(!this.body.containsValue(value)) return false;
        }
        return true;
    }

    @Override
    public boolean containsAllValue(Collection<? extends V> values) {
        if(values == null || values.isEmpty()) return false;
        if(body == null || body.isEmpty()) return false;
        for (V value : values) if(!this.body.containsValue(value)) return false;
        return true;
    }

    @Override
    public boolean containsAllValue(OwnRows<? extends V> values) {
        if(values == null || values.isEmpty()) return false;
        if(body != null && !body.isEmpty()) {
            for (V value : values) if(!this.body.containsValue(value)) return false;
            return true;
        }
        return false;
    }

    @Override
    public boolean containsAllValue(Iterable<? extends V> values) {
        if(values == null) return false;
        Iterator<? extends V> iterator = values.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(!body.containsValue(iterator.next())) return false;
        }
        return true;
    }

    @Override
    public boolean containsAnyValue(V[] values) {
        if(body == null || body.isEmpty()) return false;
        for(V value : values) {
            if(this.body.containsValue(value)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyValue(Collection<? extends V> values) {
        if(values == null || values.isEmpty()) return false;
        if(body == null || body.isEmpty()) return false;
        for (V value : values) if(this.body.containsValue(value)) return true;
        return false;
    }

    @Override
    public boolean containsAnyValue(OwnRows<? extends V> values) {
        if(values == null || values.isEmpty()) return false;
        if(body != null && !body.isEmpty()) {
            for (V value : values) if(this.body.containsValue(value)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyValue(Iterable<? extends V> values) {
        if(values == null) return false;
        Iterator<? extends V> iterator = values.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(body.containsValue(iterator.next())) return true;
        }
        return false;
    }

    @Override
    public boolean isEmpty() {
        return body == null || body.isEmpty();
    }

    @Override
    public int size() {
        return (this.body == null) ? 0 : this.body.size();
    }

    @Override
    public Set<Map.Entry<K, V>> entrySet() {
        if(this.body == null) return new HashSet<>(0);
        return this.body.entrySet();
    }

    @Override
    public Set<K> keySet() {
        if(this.body == null) return new HashSet<>(0);
        return this.body.keySet();
    }

    @Override
    public boolean initialized() {
        return body != null;
    }

    @Override
    public Collection<V> values() {
        if(this.body == null) return new ArrayList<>(0);
        return this.body.values();
    }

    @Override
    public void clear() {
        if(body != null && !body.isEmpty()) {
            this.body.clear();
        }
    }

    @Override
    public MapEnum getImplEnum() {
        return this.implEnum;
    }

    @Override
    public Comparator<? super K> getComparator() {
        return this.comparator;
    }



    @Override
    public Map<K,V> toMap() {
        return (this.body == null)
                ? this.implEnum.newMap()
                : this.implEnum.newMap(body);
    }

    @Override
    public Map<K,V> toMap(MapEnum implEnum) {
        if(implEnum == null) throw new NullPointerException("无法使用 null 的 MapEnum 实例来构建 Map 集合");
        return (this.body == null)
                ? implEnum.newMap(0)
                : implEnum.newMap(body);
    }

    @Override
    public void copyTo(Map<K, V> map) {
        if(map == null) return;
        map.putAll(this.body);
    }

    @Override
    public OnlyReadHashMap<K, V> toOnlyReadHashMap() {
        if(this.body == null || this.body.isEmpty()) return new OnlyReadHashMap<>();
        return new OnlyReadHashMap<>(this.body);
    }

    @Override
    public OnlyReadLinkedMap<K, V> toOnlyReadLinkedMap() {
        if(this.body == null || this.body.isEmpty()) return new OnlyReadLinkedMap<>();
        return new OnlyReadLinkedMap<>(this.body);
    }

    @Override
    public OnlyReadTreeMap<K, V> toOnlyReadTreeMap() {
        if(this.body == null || this.body.isEmpty()) return new OnlyReadTreeMap<>();
        return new OnlyReadTreeMap<>(this.body, this.comparator);
    }

    @Override
    public OnlyReadTreeMap<K, V> toOnlyReadTreeMap(Comparator<? super K> comparator) {
        if(this.body == null || this.body.isEmpty()) return new OnlyReadTreeMap<>();
        return new OnlyReadTreeMap<>(this.body, comparator);
    }

    @Override
    public String toString() {
        return (this.body == null) ? "{}" : this.body.toString();
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if(o instanceof AbsOwnPair) {
            return Objects.equals(body, ((AbsOwnPair<?, ?>) o).body);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(body);
    }


    /* *******************************************************************************************
     *
     *          自定义方法
     *
     * *******************************************************************************************
     * */


    public TreeMap<K,V> sortToTreeMap(Comparator<? super K> c) {
        return (TreeMap<K, V>) MapEnum.TREE.newMap(body, c);
    }

    public ConcurrentSkipListMap<K, V> sortToConcurrentSkipListMap(Comparator<? super K> c) {
        return (ConcurrentSkipListMap<K, V>) MapEnum.SKIP_LIST.newMap(body, c);
    }

    public V getOrDefaultAndPut(Object key, V defaultValue) {
        if(body == null || body.isEmpty()) return null;
        if(body.containsKey(key)) {
            return body.get(key);
        }
        this.body.put((K) key, defaultValue);
        return defaultValue;
    }


    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        if(body == null) return;
        this.body.replaceAll(function);
    }


    public void forEach(BiConsumer<? super K, ? super V> action) {
        if(body == null) return;
        this.body.forEach(action);
    }

    public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        if(body == null) return null;
        return this.body.compute(key, remappingFunction);
    }

    public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
        if(body == null) return null;
        return this.body.computeIfAbsent(key, mappingFunction);
    }


    public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        if(body == null) return null;
        return this.body.computeIfPresent(key, remappingFunction);
    }

    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        if(body == null) return null;
        return this.body.merge(key, value, remappingFunction);
    }


    /* *******************************************************************************************
     *
     *          特定实现类的转有方法
     *
     * *******************************************************************************************
     * */

    public Enumeration<V> elements() {
        if(body instanceof ConcurrentHashMap) {
            return ((ConcurrentHashMap<K,V>) body).elements();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public Enumeration<K> keys() {
        if(body instanceof ConcurrentHashMap) {
            return ((ConcurrentHashMap<K,V>) body).keys();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public ConcurrentHashMap.KeySetView<K, V> keySetView() {
        if(body instanceof ConcurrentHashMap) {
            return ((ConcurrentHashMap<K,V>) body).keySet();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public ConcurrentHashMap.KeySetView<K, V> keySetView(V mappedValue) {
        if(body instanceof ConcurrentHashMap) {
            return ((ConcurrentHashMap<K,V>) body).keySet(mappedValue);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public long mappingCount() {
        if(body instanceof ConcurrentHashMap) {
            return ((ConcurrentHashMap<K,V>) body).mappingCount();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public void forEach(long parallelismThreshold, BiConsumer<? super K,? super V> action) {
        if(body instanceof ConcurrentHashMap) {
            ((ConcurrentHashMap<K,V>) body).forEach(parallelismThreshold, action);
            return;
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public void forEachEntry(long parallelismThreshold, Consumer<? super Map.Entry<K, V>> action) {
        if(body instanceof ConcurrentHashMap) {
            ((ConcurrentHashMap<K,V>) body).forEachEntry(parallelismThreshold, action);
            return;
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public void forEachKey(long parallelismThreshold, Consumer<? super K> action) {
        if(body instanceof ConcurrentHashMap) {
            ((ConcurrentHashMap<K,V>) body).forEachKey(parallelismThreshold, action);
            return;
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public void forEachValue(long parallelismThreshold, Consumer<? super V> action) {
        if(body instanceof ConcurrentHashMap) {
            ((ConcurrentHashMap<K,V>) body).forEachValue(parallelismThreshold, action);
            return;
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.concurrent.ConcurrentHashMap " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("如果子类是懒加载模式初始化的 body 属性（即构造的 initBody 是 false），" +
            "那么这里可以提供初始化")
    protected void protected_lazyInitBody(int initSize, MapEnum implEnum) {
        if(body == null) {
            this.implEnum = implEnum;
            this.body = (this.implEnum == MapEnum.TREE || this.implEnum == MapEnum.SKIP_LIST)
                    ? this.implEnum.newMap(comparator)
                    : this.implEnum.newMap(initSize);
        }
    }

    @Note("Map 集合实现类枚举不能为 null")
    private void private_implNotNull(MapEnum impl) {
        if(impl == null) throw new IllegalArgumentException("若想指定内部容器的初始化的实现类型，则 MapEnum 枚举不能为 null");
    }

    @Note("put 单个元素的逻辑")
    protected void protected_putOne(K key, V value) {
        this.body.put(key, value);
    }

    @Note("putIfAbsent 单个元素的逻辑")
    protected void protected_putIfAbsent(K sourceKey, V backupValue) {
        this.body.putIfAbsent(sourceKey, backupValue);
    }

    @Note("put 多个元素的逻辑")
    protected void protected_putAll(Map<? extends K, ? extends V> map) {
        if(map == null || map.isEmpty()) return;
        this.body.putAll(map);
    }

    @Note("put 多个元素的逻辑")
    protected void protected_putAll(OwnPairs<? extends K, ? extends V> pair) {
        if(pair == null || pair.isEmpty()) return;
        for (Map.Entry<? extends K, ? extends V> e : pair.entrySet()) {
            this.body.put(e.getKey(), e.getValue());
        }
    }

    @Note("remove 单个元素的逻辑")
    protected void protected_removeOne(Object key) {
        if(body == null || body.isEmpty()) return;
        this.body.remove(key);
    }

    @Note("remove 多个元素的逻辑")
    protected void protected_removeAll(Object[] keys) {
        if(keys == null || keys.length == 0) return;
        for (Object key : keys) {
            this.body.remove(key);
        }
    }

    @Note("remove 多个元素的逻辑")
    protected void protected_removeAll(Iterable<?> keys) {
        if(keys == null) return;
        for (Object key : keys) {
            this.body.remove(key);
        }
    }

    @Note("remove 单个元素的逻辑")
    protected void protected_removeOne(Object key, V value) {
        if(body == null || body.isEmpty()) return;
        this.body.remove(key, value);
    }

    @Note("remove 多个元素的逻辑")
    protected void protected_removeAll(Map<? extends K, ? extends V> map) {
        if(map == null || map.isEmpty()) return;
        for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
            this.body.remove(e.getKey(), e.getValue());
        }
    }

    @Note("remove 多个元素的逻辑")
    protected void protected_removeAll(OwnPairs<? extends K, ? extends V> pair) {
        if(pair != null && !pair.isEmpty())  {
            for (Map.Entry<? extends K, ? extends V> e : pair.entrySet()) {
                this.body.remove(e.getKey(), e.getValue());
            }
        }
    }

    @Note("replace 私有逻辑")
    protected void protected_replace(K key, V newValue) {
        if(body == null || body.isEmpty()) return;
        this.body.replace(key, newValue);
    }

    @Note("replace 私有逻辑")
    protected void protected_replace(K key, V oldValue, V newValue) {
        this.body.replace(key, oldValue, newValue);
    }
}
